﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Plugin.Core
{
    /// <summary>
    /// This attribute use for define slots of any plug-ins
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
    public sealed class SlotAttribute : Attribute
    {
        public SlotAttribute(string SlotName)
        {
            this.SlotName = SlotName;
        }

        public string SlotName { get; set; }

        public static SlotAttribute FromPlugin(IPlugin plugin)
        {
            object[] attributes = plugin.GetType().GetCustomAttributes(typeof(SlotAttribute), false);
            if (attributes.Length > 0)
                return attributes[0] as SlotAttribute;
            return null;
        }
    }
    /// <summary>
    /// This attribute use for define exts of any plug-ins
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
    public sealed class ExtentionAttribute : Attribute
    {
        public ExtentionAttribute(string ExtentionName)
        {
            this.ExtentionName = ExtentionName;
        }

        public string ExtentionName { get; set; }


        public static List<ExtentionAttribute> FromPlugin(IPlugin plugin)
        {
            List<ExtentionAttribute> Result = new List<ExtentionAttribute>();
            object[] attributes = plugin.GetType().GetCustomAttributes(typeof(ExtentionAttribute), false);
            if (attributes.Length > 0)
                foreach (object attribute in attributes)
                    Result.Add((ExtentionAttribute)attribute);
            return Result;
        }
    }
    /// <summary>
    /// This attribute can be used for set any plug-in info into that plug-in
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
    public sealed class PluginInfoAttribute : Attribute
    {

        public PluginInfoAttribute(string Title, string Descreption, string Author, string Company, string DateCreated, string Version)
        {
            _title = Title;
            _descreption = Descreption;
            _author = Author;
            _company = Company;
            _dateCreated = DateTime.Parse(DateCreated);
            _version = new Version(Version);
        }

        private Version _version = null;

        private string _title = string.Empty;

        private string _descreption = string.Empty;

        private string _author = string.Empty;

        private string _company = string.Empty;

        private DateTime _dateCreated;

        public string Title { get { return _title; } }

        public string Descreption { get { return _descreption; } }

        public Version Version { get { return _version; } }

        public string Author { get { return _author; } }

        public string Company { get { return _company; } }

        public DateTime DateCreated { get { return _dateCreated; } }

        public static PluginInfoAttribute FromPlugin(IPlugin plugin)
        {
            object[] attributes = plugin.GetType().GetCustomAttributes(typeof(PluginInfoAttribute), false);
            if (attributes.Length > 0)
                return attributes[0] as PluginInfoAttribute;
            return null;
        }

    }
    /// <summary>
    /// This attribute views type and work type of any plug-in
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
    public sealed class PluginSettingsAttribute : Attribute
    {
        public bool Run { get; set; }

        public bool IsControl { get; set; }

        public static PluginSettingsAttribute FromPlugin(IPlugin plugin)
        {
            object[] attributes = plugin.GetType().GetCustomAttributes(typeof(PluginSettingsAttribute), false);
            if (attributes.Length > 0)
                return attributes[0] as PluginSettingsAttribute;
            return null;
        }

    }
    /// <summary>
    /// This interface is base of any config class
    /// </summary>
    public interface IConfigoration : IDictionary<string, object>
    {
        void Save(params object[] Parameters);
        void Load(params object[] Parameters);
        bool Exist(string Key);
    }
    /// <summary>
    /// This interface is base of any pluf-in that created by users
    /// </summary>
    public interface IPlugin
    {
        IPlugins Plugins { get; }
        IPlugin Parent { get; set; }
        object Value { get; set; }
        IConfigoration Config { get; set; }
        void Run(params object[] parameters);
    }
    /// <summary>
    /// This class use with "Plug-in Engine" to run system
    /// </summary>
    public interface IHost
    {
        IPlugins Plugins { get; }

        IConfigoration Config { get; set; }

        void Run(params object[] Parameters);
    }
    /// <summary>
    /// This is a collection of plug-ins that use for child 
    /// plug-ins
    /// </summary>
    public class IPlugins : List<IPlugin>
    {
        public int[] GetExtentionIndexes(SlotAttribute slot)
        {
            return GetExtentionIndexes(slot.SlotName);
        }

        public int[] GetExtentionIndexes(string ExtentionName)
        {
            List<int> result = new List<int>();
            for (int i = 0; i < Count; i++)
            {
                List<ExtentionAttribute> extentions = ExtentionAttribute.FromPlugin(this[i]);
                if (extentions.Count == 0) continue;
                bool find = extentions.Exists(
                    new Predicate<ExtentionAttribute>
                    (
                      delegate(ExtentionAttribute extention)
                      {
                          return (extention.ExtentionName == ExtentionName);
                      }
                    ));
                if (find) result.Add(i);
            }
            return result.ToArray();
        }


    }
    /// <summary>
    /// This is sample host that its enough for run system
    /// with engine.
    /// </summary>
    public class Host : IHost
    {
        private IPlugins _Plugins = new IPlugins();

        public IConfigoration Config { get; set; }

        public IPlugins Plugins
        {
            get { return _Plugins; }
        }

        private void CreateConnections()
        {
            foreach (IPlugin plugin in Plugins)
            {
                SlotAttribute slot = SlotAttribute.FromPlugin(plugin);
                int[] ExtentionIndexes = Plugins.GetExtentionIndexes(slot);
                if (ExtentionIndexes.Length == 0) continue;
                foreach (int ExtentionIndex in ExtentionIndexes)
                {
                    plugin.Plugins.Add(Plugins[ExtentionIndex]);
                    Plugins[ExtentionIndex].Parent = plugin;
                }
            }
        }

        public void Run(params object[] Parameters)
        {
            CreateConnections();

            foreach (IPlugin plugin in Plugins)
            {
                PluginSettingsAttribute setting = PluginSettingsAttribute.FromPlugin(plugin);
                if (setting == null) continue;
                if (setting.Run)
                    plugin.Run();
            }
        }

    }

}
